package hku.cs.eclunuo.page;

import hku.cs.eclunuo.LunuoConstant;
import hku.cs.eclunuo.LunuoLog;
import hku.cs.eclunuo.LunuoPlugin;
import hku.cs.eclunuo.nature.SootNature;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.ui.util.TableLayoutComposite;
import org.eclipse.jdt.ui.ISharedImages;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ContentViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.osgi.util.TextProcessor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IWorkbenchPropertyPage;
import org.eclipse.ui.dialogs.PropertyPage;



public class ProjectPropertyPage extends PropertyPage implements
		IWorkbenchPropertyPage {
	
	
	private static final Image IMG_PKG= JavaUI.getSharedImages().getImage(ISharedImages.IMG_OBJS_PACKAGE);
	private Button autoBuilderCheckBox;
	private Button lightBuilderCheckBox;
	
	private Label fFilterViewerLabel;
	private CheckboxTableViewer fFilterViewer;
	private Table fFilterTable;

	private Button selectAllButton;
	private Button deselectAllButton;
	


	private FilterContentProvider fFilterContentProvider;
	
	public ProjectPropertyPage() {
		
	}

	/**
	 * Model object that represents a single entry in the filter table.
	 */
	private static class Filter {

		private String fName;
		private boolean fChecked;

		public Filter(String name, boolean checked) {
			setName(name);
			setChecked(checked);
		}

		public String getName() {
			return fName;
		}
		
		public void setName(String name) {
			fName= name;
		}
				
		public boolean isChecked() {
			return fChecked;
		}

		public void setChecked(boolean checked) {
			fChecked= checked;
		}

		public boolean equals(Object o) {
			if (!(o instanceof Filter))
				return false;

			Filter other= (Filter) o;
			return (getName().equals(other.getName()));
		}

		public int hashCode() {
			return fName.hashCode();
		}
	}

	/**
	 * Sorter for the filter table; sorts alphabetically ascending.
	 */
	private static class FilterViewerSorter extends ViewerComparator {
		public int compare(Viewer viewer, Object e1, Object e2) {
			ILabelProvider lprov= (ILabelProvider) ((ContentViewer) viewer).getLabelProvider();
			String name1= lprov.getText(e1);
			String name2= lprov.getText(e2);
			if (name1 == null)
				name1= ""; //$NON-NLS-1$

			if (name2 == null)
				name2= ""; //$NON-NLS-1$

			if (name1.length() > 0 && name2.length() > 0) {
				boolean isPackage1= name1.indexOf('*') != -1;
				boolean isPackage2= name2.indexOf('*') != -1;
				if (isPackage1 && !isPackage2)
					return -1;

				if (isPackage2 && !isPackage1)
					return 1;
			}
			return getComparator().compare(name1, name2);
		}
	}

	/**
	 * Label provider for Filter model objects
	 */
	private static class FilterLabelProvider extends LabelProvider implements ITableLabelProvider {

		public String getColumnText(Object object, int column) {
			return (column == 0) ? getText(object) : ""; //$NON-NLS-1$
		}

		public String getText(Object element) {
			return TextProcessor.process(((Filter) element).getName());
		}

		public Image getColumnImage(Object object, int column) {
			return IMG_PKG;
		}
	}

	/**
	 * Content provider for the filter table.  Content consists of instances of
	 * Filter.
	 */
	private class FilterContentProvider implements IStructuredContentProvider {
		private List<Filter> fFilters;
		
		public FilterContentProvider() {
			List<String> inactive= Arrays.asList(getFilterPropertyValue());
			List<String> active = null;
			//compute active & inactive list
			IJavaProject ijp  = getJavaProject();	
//			System.out.println(inactive.size());
			active = new ArrayList<String>();
			try {
				IPackageFragmentRoot[] ipfs = null;
				IClasspathEntry xx[]= ijp.getRawClasspath();
				
				for(int i=0; i<xx.length; i++)
					if (xx[i].getEntryKind() == IClasspathEntry.CPE_SOURCE){
						ipfs = ijp.findPackageFragmentRoots(xx[i]);
						for(int z=0; z<ipfs.length; z++){
							IJavaElement[] a = ipfs[z].getChildren();
							for(int j=0; j<a.length; j++){
								IResource[] irs = ((IFolder)a[j].getCorrespondingResource()).members(IResource.FILE);
								boolean hasJava = false;
								for(IResource iFile: irs){
									if("java".equals(iFile.getFileExtension()))
										hasJava=true;
								}
								String pack = a[j].getElementName();
								if("".equals(pack))
									pack = LunuoConstant.DEFAULTPACKNAME;
								if(hasJava&&!inactive.contains(pack)){
									active.add(pack);
								}
							}
						}
					}	
			} catch (JavaModelException e) {
				e.printStackTrace();
			} catch (CoreException e) {
				e.printStackTrace();
			}	
			//------------------------
			
			populateFilters(active, inactive);
		}
		
		protected void populateFilters(List<String> activeList, List<String> inactiveList) {
			fFilters= new ArrayList<Filter>(activeList.size() + inactiveList.size());
			populateList(activeList, true);
			if (inactiveList.size() != 0)
				populateList(inactiveList, false);
		}
		
		protected void populateList(List<String> list, boolean checked) {
			Iterator<String> iterator= list.iterator();

			while (iterator.hasNext()) {
				String name= (String) iterator.next();
				addFilter(name, checked);
			}
		}
		
		public Filter addFilter(String name, boolean checked) {
			Filter filter= new Filter(name, checked);
			if (!fFilters.contains(filter)) {
				fFilters.add(filter);
				fFilterViewer.add(filter);
				fFilterViewer.setChecked(filter, checked);
			}
			updateActions();
			return filter;
		}
		
		public void saveFilterPropertyValue() {
			List<String> inactive= new ArrayList<String>(fFilters.size());
			Iterator<Filter> iterator= fFilters.iterator();
			while (iterator.hasNext()) {
				Filter filter= (Filter) iterator.next();
				String name= filter.getName();
				if (!filter.isChecked())
					inactive.add(name);
			}		
			IResource resource = (IResource) getElement().getAdapter(
					IResource.class);
			StringBuffer value = new StringBuffer();
			String strValue;
			if(inactive.size()==0)
				strValue = null;
			else{
				for(int i=0; i<inactive.size(); i++)
					value.append(inactive.get(i)+";");
				strValue = value.toString();
			}
			
			try {
				resource.setPersistentProperty(LunuoPlugin.FILTER_NOINSTRU_TARGET_PROPERTY,
						strValue);
			} catch (CoreException e) {
				LunuoLog.logError(e);
			}
			
		}
		
		protected String[] getFilterPropertyValue() {
			IResource resource = (IResource) getElement().getAdapter(
					IResource.class);
			try {
				String value = resource
						.getPersistentProperty(LunuoPlugin.FILTER_NOINSTRU_TARGET_PROPERTY);
				if (value == null) {
					return new String[0];
				}
				return value.split(";");
			} catch (CoreException e) {
				LunuoLog.logError(e);
				return null;
			}
		}

		public Object[] getElements(Object inputElement) {
			return fFilters.toArray();
		}
		
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {}
		public void dispose() {}

		
		public void toggleFilter(Filter filter) {
			boolean newState= !filter.isChecked();
			filter.setChecked(newState);
			fFilterViewer.setChecked(filter, newState);
		}

		

	}

	protected Control createContents(Composite parent) {
		noDefaultAndApplyButton();
		Composite composite= new Composite(parent, SWT.NULL);
		GridLayout layout= new GridLayout();
		layout.numColumns= 1;
		layout.marginHeight= 0;
		layout.marginWidth= 0;
		composite.setLayout(layout);
		GridData data= new GridData();
		data.verticalAlignment= GridData.FILL;
		data.horizontalAlignment= GridData.FILL;
		composite.setLayoutData(data);

		createAutoBuilderCheckbox(composite);
		createLightBuilderCheckbox(composite);
		createFilterPreferences(composite);
		Dialog.applyDialogFont(composite);
		return composite;
	}

	private void createFilterPreferences(Composite composite) {
		fFilterViewerLabel= new Label(composite, SWT.SINGLE | SWT.LEFT);
		fFilterViewerLabel.setText("Choose the package apply the instrumenting");
		
		Composite container= new Composite(composite, SWT.NONE);
		GridLayout layout= new GridLayout();
		layout.numColumns= 2;
		layout.marginHeight= 0;
		layout.marginWidth= 0;
		container.setLayout(layout);
		GridData gd= new GridData(GridData.FILL_BOTH);
		container.setLayoutData(gd);

		createFilterTable(container);
		createStepFilterButtons(container);
	}

	private void createStepFilterButtons(Composite container) {
		Composite buttonContainer= new Composite(container, SWT.NONE);
		GridData gd= new GridData(GridData.FILL_VERTICAL);
		buttonContainer.setLayoutData(gd);
		GridLayout buttonLayout= new GridLayout();
		buttonLayout.numColumns= 1;
		buttonLayout.marginHeight= 0;
		buttonLayout.marginWidth= 0;
		buttonContainer.setLayout(buttonLayout);

		selectAllButton= new Button(buttonContainer, SWT.PUSH);
		selectAllButton.setText("Select All");
		selectAllButton.setToolTipText("Select All File in Project");
		gd= new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING);
		selectAllButton.setLayoutData(gd);
		selectAllButton.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event e) {
				checkAllFilters(true);
			}
		});

		deselectAllButton= new Button(buttonContainer, SWT.PUSH);
		deselectAllButton.setText("Deselect All");
		deselectAllButton.setToolTipText("Deelect All File in Project");
		gd= getButtonGridData(deselectAllButton);
		deselectAllButton.setLayoutData(gd);
		deselectAllButton.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event e) {
				checkAllFilters(false);
			}
		});
		
	}

	private void checkAllFilters(boolean check) {
		Object[] filters= fFilterContentProvider.getElements(null);
		for (int i= (filters.length - 1); i >= 0; --i)
			 ((Filter) filters[i]).setChecked(check);

		fFilterViewer.setAllChecked(check);
	}
	
	private void createFilterTable(Composite container) {
		TableLayoutComposite layouter= new TableLayoutComposite(container, SWT.NONE);
		layouter.addColumnData(new ColumnWeightData(100));
		layouter.setLayoutData(new GridData(GridData.FILL_BOTH));
		
		fFilterTable= new Table(layouter, SWT.CHECK | SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);

		
//		fFilterTable= new Table(container, SWT.CHECK | SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);

		new TableColumn(fFilterTable, SWT.NONE);
		fFilterViewer= new CheckboxTableViewer(fFilterTable);
		fFilterViewer.setLabelProvider(new FilterLabelProvider());
		fFilterViewer.setComparator(new FilterViewerSorter());
		fFilterContentProvider= new FilterContentProvider();
		fFilterViewer.setContentProvider(fFilterContentProvider);
		// input just needs to be non-null
		fFilterViewer.setInput(this);
		fFilterViewer.addCheckStateListener(new ICheckStateListener() {
			public void checkStateChanged(CheckStateChangedEvent event) {
				Filter filter= (Filter) event.getElement();
				fFilterContentProvider.toggleFilter(filter);
			}
		});
	}
	
	


	private void createAutoBuilderCheckbox(Composite container) {
		autoBuilderCheckBox= new Button(container, SWT.CHECK | SWT.WRAP);
		autoBuilderCheckBox.setText("Auto-Builder");
		autoBuilderCheckBox.setToolTipText("If this button checked on, every time you save " +
			"source file will call Lunuo Instrument AUTOMATICALLY");
		GridData gd= getButtonGridData(autoBuilderCheckBox);
		autoBuilderCheckBox.setLayoutData(gd);
		try {
			setAutoBuilderCheckBoxSelection(getProject().hasNature(SootNature.NATURE_ID));
		} catch (CoreException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Programatic access to enable assertions checkbox
	 * @return boolean indicating check box selected or not
	 */
	public boolean getAutoBuilderCheckBoxSelection() {
		return autoBuilderCheckBox.getSelection();
	}

	public void setAutoBuilderCheckBoxSelection(boolean selected) {
		autoBuilderCheckBox.setSelection(selected);
	}
	
	
	private void createLightBuilderCheckbox(Composite container) {
		lightBuilderCheckBox= new Button(container, SWT.CHECK | SWT.WRAP);
		lightBuilderCheckBox.setText("Light-Builder");
		lightBuilderCheckBox.setToolTipText("If this button checked on, it will resolve memory out problem");
		GridData gd= getButtonGridData(lightBuilderCheckBox);
		lightBuilderCheckBox.setLayoutData(gd);
		try {
			IResource resource = (IResource) getElement().getAdapter(
					IResource.class);
			String value = resource
			.getPersistentProperty(LunuoPlugin.LIGHT_BUILDER);
			if(value==null || value.length()==0)
				setLightBuilderCheckBoxSelection(false);
			else
				setLightBuilderCheckBoxSelection(true);
		} catch (CoreException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Programatic access to enable assertions checkbox
	 * @return boolean indicating check box selected or not
	 */
	public Boolean getLightBuilderCheckBoxSelection() {
		return lightBuilderCheckBox.getSelection();
	}

	public void setLightBuilderCheckBoxSelection(boolean selected) {
		lightBuilderCheckBox.setSelection(selected);
	}

	private GridData getButtonGridData(Button button) {
		GridData gd= new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING);
		int widthHint= convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
		gd.widthHint= Math.max(widthHint, button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
		return gd;
	}	
	
	private IJavaProject getJavaProject() {
		return (IJavaProject)getElement();
		
	}
	
	private IProject getProject(){
		return ((IJavaProject)getElement()).getProject();
	}
	
	public boolean performOk() {
		if (autoBuilderCheckBox.getSelection())
			LunuoPlugin.getDefault().addSootNature(getProject(), true);
		else
			LunuoPlugin.getDefault().removeSootNature(getProject());
		
		IResource resource = (IResource) getElement().getAdapter(
				IResource.class);
		
			try {
				if(getLightBuilderCheckBoxSelection())
					resource.setPersistentProperty(LunuoPlugin.LIGHT_BUILDER,
						"LIGHT_BUILDER");
				else
					resource.setPersistentProperty(LunuoPlugin.LIGHT_BUILDER
						, "");
			} catch (CoreException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
		
		fFilterContentProvider.saveFilterPropertyValue();
		
		return true;
	}
	
	protected void updateActions() {
		if (selectAllButton == null || deselectAllButton == null)
			return;

		boolean enabled= fFilterViewer.getTable().getItemCount() > 0;
		selectAllButton.setEnabled(enabled);
		deselectAllButton.setEnabled(enabled);
	}
}
